Conversation
There was a problem hiding this comment.
LGTM — routine automated version bump from the Changesets action.
Extended reasoning...
Overview
This PR touches three files: .changeset/pre.json (adds the zod-json-schema-compat changeset entry), packages/core/CHANGELOG.md (adds the 2.0.0-alpha.2 release section), and packages/core/package.json (bumps the version from 2.0.0-alpha.1 to 2.0.0-alpha.2). All changes are auto-generated by the Changesets release action.
Security risks
None. These are purely metadata/versioning files with no executable logic.
Level of scrutiny
Minimal scrutiny required. Auto-generated version bump PRs from the Changesets bot are among the simplest, most mechanical changes in a repo — the content is derived deterministically from merged changesets.
Other factors
No bugs were found by the bug hunting system, no prior review comments are outstanding, and the diff is a textbook Changesets alpha pre-release bump. Safe to approve.
35992ec to
1aaa39d
Compare
There was a problem hiding this comment.
LGTM — standard auto-generated Changesets release PR with only version bumps and CHANGELOG additions.
Extended reasoning...
Overview
This is an automated "Version Packages (alpha)" PR generated by the Changesets release action. It touches five files: .changeset/pre.json (adds two new changeset IDs), packages/client/CHANGELOG.md and packages/core/CHANGELOG.md (CHANGELOG entries), and packages/client/package.json / packages/core/package.json (version bumps only — 2.0.0-alpha.2 → 2.0.0-alpha.3 for client, 2.0.0-alpha.1 → 2.0.0-alpha.2 for core). No production logic is changed.
Security risks
None. The diff contains no code — only version strings, CHANGELOG prose, and changeset bookkeeping. No auth, crypto, or permissions paths are touched.
Level of scrutiny
Minimal scrutiny warranted. Automated release PRs of this kind are fully deterministic outputs of changeset version; the actual code changes they describe landed in earlier commits and were reviewed there.
Other factors
Two inline bug comments are attached. The first is a nit about CHANGELOG wording inverting the Accept header ordering described in PR #1655 — doc-only, no functional impact. The second is a pre-existing inconsistency in packages/core/package.json (zod listed in both dependencies and peerDependencies) that was not introduced by this PR and has no npm consumer impact since core is private: true. Neither issue warrants blocking this release PR.
| Accept headers to spec-required defaults in StreamableHTTPClientTransport | ||
|
|
||
| Custom Accept headers provided via `requestInit.headers` are now appended to the spec-mandated Accept types instead of being overwritten. This ensures the required media types (`application/json, text/event-stream` for POST; `text/event-stream` for GET SSE) are always present | ||
| while allowing users to include additional types for proxy/gateway routing. | ||
|
|
There was a problem hiding this comment.
🟡 The CHANGELOG entry says custom Accept headers are "appended to the spec-mandated Accept types", implying spec-required types come first, but the actual implementation does the reverse: user-provided types are placed first and spec-required types are appended last. The description should be corrected to accurately reflect the ordering, e.g., "spec-mandated Accept types are appended to any custom Accept headers".
Extended reasoning...
CHANGELOG Wording Inverts Actual Accept Header Ordering
What the bug is and how it manifests
The new CHANGELOG entry (line 10) states: "Custom Accept headers provided via requestInit.headers are now appended to the spec-mandated Accept types instead of being overwritten." In standard English, "X appended to Y" means Y is the base and X is added after — so this sentence implies spec-required types come first, with user custom types added afterward.
However, the actual implementation in streamableHttp.ts does the opposite. The GET SSE path uses: [...(userAccept?.split(',').map(s => s.trim().toLowerCase()) ?? []), 'text/event-stream'] and the POST path uses [...(userAccept?.split(',') ?? []), 'application/json', 'text/event-stream']. User-provided types come first, and spec-required types are appended last.
The specific code path and why it matters
Per RFC 7231, when multiple Accept types share equal q-values, earlier entries carry higher implicit preference. So the actual behavior gives user-provided types higher preference than spec-required types. The CHANGELOG implies the opposite preference ordering. While this ordering may be intentional (user routing hints for proxies need higher preference), the documentation still misrepresents which type comes first.
Addressing the refutation
The refutation argues that user-types-first is intentional for proxy/gateway routing, that CHANGELOG entries are not formal specs, and that this is a version-bump PR. These points are valid context, but they do not resolve the wording inaccuracy. The key claim in the CHANGELOG — that spec-required types are always present — is correct, but the described ordering is backwards. The fix is a one-line wording update, not a code change. Since this CHANGELOG entry appears in this PR's diff, this is the appropriate place to catch it.
Step-by-step proof of the discrepancy
- User sets
requestInit.headers = { Accept: 'application/vnd.proxy-hint' } - Code computes
types = ['application/vnd.proxy-hint', 'text/event-stream'] - Final header:
Accept: application/vnd.proxy-hint, text/event-stream application/vnd.proxy-hinthas higher implicit preference (appears first)- CHANGELOG says custom types are "appended to" spec types, which would produce
text/event-stream, application/vnd.proxy-hint(spec first, custom second) - The actual and described orderings are reversed.
Impact and fix
This is a documentation-only inaccuracy — the functional behavior is correct (spec-required types are always present). The fix is to update the CHANGELOG wording to something like: "spec-mandated Accept types are appended to any custom Accept headers provided via requestInit.headers", which accurately reflects user types appearing first with spec types appended after. The secondary q-value deduplication issue (e.g., text/event-stream; q=0.9 not deduped against text/event-stream) is a pre-existing edge case from PR #1655 and not introduced by this PR.
| "name": "@modelcontextprotocol/core", | ||
| "private": true, | ||
| "version": "2.0.0-alpha.1", | ||
| "version": "2.0.0-alpha.2", |
There was a problem hiding this comment.
🟣 Pre-existing: packages/core/package.json lists zod in both dependencies (auto-installs) and peerDependencies with optional: false (signals manual install required) — these are contradictory. The drop-zod-peer-dep changeset cleaned this up for the public client and server packages but missed core. Since core is private: true, there is no npm consumer impact, but the redundant peer dependency entry should be removed for consistency.
Extended reasoning...
Analysis
packages/core/package.json currently has zod in two conflicting locations:
dependencies(line 51):"zod": "catalog:runtimeShared"— this means zod is a direct dependency that auto-installs for any consumer of core.peerDependencies(line 55):"zod": "catalog:runtimeShared"— combined withpeerDependenciesMeta.zod.optional: false(line 62), this signals to package managers that users must install zod themselves as a peer.
These two declarations are semantically contradictory. A direct dependency auto-installs and requires nothing from the consumer; a non-optional peer dependency generates package-manager warnings telling consumers they must install the dependency themselves.
Why existing code does not prevent this
The drop-zod-peer-dep changeset (documented in packages/client/CHANGELOG.md under the 2.0.0-alpha.1 entry) explicitly dropped zod from peerDependencies in @modelcontextprotocol/client and @modelcontextprotocol/server. The changeset file targets those two packages but the same cleanup was not applied to packages/core/package.json. The version bump in this PR touches packages/core/package.json (line 4, version field) but does not address this inconsistency.
Step-by-step proof
- Open
packages/core/package.json. - Observe
"zod": "catalog:runtimeShared"underdependencies— zod will auto-install in any workspace consuming core. - Observe
"zod": "catalog:runtimeShared"underpeerDependencies— a package manager resolving this package would also emit a peer dependency warning. - Observe
peerDependenciesMeta.zod.optional: false— this marks the peer as required, not optional, making the warning an error in strict tools. - Compare with
packages/client/package.json: zod is only independencies, nopeerDependenciesentry — the cleanup was done there.
Impact
Practical impact is minimal: core is "private": true and is never published to npm, so no external consumers will encounter the contradictory peer dependency warning. Within the pnpm workspace the direct dependency satisfies the peer requirement automatically. The issue is purely a documentation/consistency problem.
Fix
Remove the zod entry from peerDependencies and its corresponding peerDependenciesMeta.zod block in packages/core/package.json, matching what was done for the client and server packages in the drop-zod-peer-dep changeset.
1aaa39d to
0ac069f
Compare
| { | ||
| "name": "@modelcontextprotocol/server", | ||
| "version": "2.0.0-alpha.2", | ||
| "version": "2.0.0-alpha.3", | ||
| "description": "Model Context Protocol implementation for TypeScript - Server package", | ||
| "license": "MIT", | ||
| "author": "Anthropic, PBC (https://anthropic.com)", |
There was a problem hiding this comment.
🔴 PR #1843 removed @cfworker/json-schema from peerDependencies of both @modelcontextprotocol/client and @modelcontextprotocol/server while simultaneously adding ./validators/cf-worker sub-path exports and workerd shims that depend on it at runtime, meaning any Cloudflare Workers consumer will get a module-not-found error. Re-add @cfworker/json-schema as an optional peerDependency in packages/client/package.json and packages/server/package.json before publishing these alpha.3 packages.
Extended reasoning...
@cfworker/json-schema removed from peerDependencies but required at runtime
What the bug is and how it manifests
PR #1843 (commit 7ba58da, merged before this version-bump PR) made two contradictory changes simultaneously: (1) it added new ./validators/cf-worker sub-path exports and workerd condition shims in both @modelcontextprotocol/client and @modelcontextprotocol/server that depend on @cfworker/json-schema, and (2) it removed @cfworker/json-schema from the peerDependencies of both published packages. As a result, any consumer who installs @modelcontextprotocol/server@2.0.0-alpha.3 or @modelcontextprotocol/client@2.0.0-alpha.3 will not have @cfworker/json-schema in their node_modules, and runtime module resolution will fail.
The specific code path that triggers it
The chain is: packages/server/src/shimsWorkerd.ts and packages/client/src/shimsWorkerd.ts export CfWorkerJsonSchemaValidator from '@modelcontextprotocol/core'. packages/core/src/validators/cfWorkerProvider.ts line 11 contains a top-level static import: . The tsdown.config.ts for both client and server uses , which inlines core code directly into the built output bundles. However, @cfworker/json-schema is NOT in noExternal — the config comment explicitly states 'treat all other dependencies as external'. Therefore, the built dist/shimsWorkerd.mjs and dist/validators/cfWorker.mjs both contain a bare external ESM import statement at the top of the file.
Why existing code doesn't prevent it
packages/core/package.json does list @cfworker/json-schema as an optional peerDependency. However, core is and is never published to npm — its peerDependencies declaration is invisible to external consumers. The only package.json files that matter to npm consumers are packages/client/package.json and packages/server/package.json, and in both of those @cfworker/json-schema appears only in devDependencies (not installed for consumers, only for local development).
Impact
There are two affected populations: (1) Any Cloudflare Workers user of either package — the workerd export condition is automatically selected by CF Workers bundlers (Wrangler), so even users who never explicitly import from /validators/cf-worker will get shimsWorkerd.mjs loaded, which immediately fails with a top-level import error. This breaks ALL CF Workers integrations. (2) Users who explicitly import from '@modelcontextprotocol/server/validators/cf-worker' or '@modelcontextprotocol/client/validators/cf-worker' in any environment also get the same failure. No package manager will warn about the missing dependency because there is no peerDependencies declaration in the published packages.
Step-by-step proof
- Developer runs — installs alpha.3 without @cfworker/json-schema (not in dependencies or peerDependencies).
- Developer deploys to Cloudflare Workers; Wrangler bundles the app and resolves the ./_shims export with the workerd condition → selects dist/shimsWorkerd.mjs.
- dist/shimsWorkerd.mjs contains: as a bare ESM import.
- Module resolution fails: .
- Deployment fails or the worker crashes on startup with a module-not-found error.
How to fix
Re-add @cfworker/json-schema as an optional peerDependency in both packages/client/package.json and packages/server/package.json, matching the pattern used before PR #1843:
This ensures package managers warn CF Workers users to install the dependency and makes the runtime requirement explicit in the published package metadata.
| - [#1788](https://github.com/modelcontextprotocol/typescript-sdk/pull/1788) [`df4b6cc`](https://github.com/modelcontextprotocol/typescript-sdk/commit/df4b6cc88d6f24fc857519cf506a7a039f532637) Thanks [@claygeo](https://github.com/claygeo)! - Prevent stack overflow in | ||
| StreamableHTTPServerTransport.close() with re-entrant guard |
There was a problem hiding this comment.
🟡 The CHANGELOG entry for 2.0.0-alpha.3 references a non-existent class name StreamableHTTPServerTransport.close(); the actual class is WebStandardStreamableHTTPServerTransport (there is also a separate NodeStreamableHTTPServerTransport). Update the entry to use the full, unambiguous class name to match both the codebase and the other entry in the same file.
Extended reasoning...
What the bug is
The new CHANGELOG entry in packages/server/CHANGELOG.md (lines 7–8) reads:
Prevent stack overflow in StreamableHTTPServerTransport.close() with re-entrant guard
No class named StreamableHTTPServerTransport exists anywhere in the codebase. The abbreviated name was informally used in the changeset file (fix-streamable-close-reentrant.md) and propagated into the CHANGELOG by the Changesets tooling.
The specific code that was changed
The re-entrancy fix (a _closed flag check) was applied to WebStandardStreamableHTTPServerTransport.close() in packages/server/src/server/streamableHttp.ts around line 224. A separate NodeStreamableHTTPServerTransport exists in packages/middleware/node. The abbreviated name is ambiguous between both implementations.
Why existing validation doesn't catch it
Changesets generates CHANGELOG prose from the markdown body of changeset files. The changeset author used the shorthand StreamableHTTPServerTransport, and no lint rule or automated check validates class name accuracy inside CHANGELOG prose. The error therefore passed through undetected.
Impact
Developers searching the CHANGELOG or codebase for StreamableHTTPServerTransport will find zero results and may conclude the entry refers to a different or removed class. The ambiguity between WebStandardStreamableHTTPServerTransport and NodeStreamableHTTPServerTransport could also cause confusion about which implementation received the fix. The other 2.0.0-alpha.2 entry in the same file (fix-onerror-callbacks) correctly names WebStandardStreamableHTTPServerTransport, making the inconsistency more jarring.
Step-by-step proof
- Open
packages/server/CHANGELOG.md, lines 7–8: entry saysStreamableHTTPServerTransport.close(). - Search the codebase for
class StreamableHTTPServerTransport— zero matches. - Search for
class WebStandardStreamableHTTPServerTransport— found inpackages/server/src/server/streamableHttp.ts:224. - Search for
class NodeStreamableHTTPServerTransport— found inpackages/middleware/node. - The same CHANGELOG file, line 99 (alpha.2 entry), correctly uses the full name
WebStandardStreamableHTTPServerTransport.
How to fix
Change the CHANGELOG entry to: "Prevent stack overflow in WebStandardStreamableHTTPServerTransport.close() with re-entrant guard".
0ac069f to
fe53cda
Compare
| ## 2.0.0-alpha.2 | ||
|
|
||
| ### Patch Changes | ||
|
|
||
| - [#1768](https://github.com/modelcontextprotocol/typescript-sdk/pull/1768) [`866c08d`](https://github.com/modelcontextprotocol/typescript-sdk/commit/866c08d3640c5213f80c3b4220e24c42acfc2db8) Thanks [@felixweinberger](https://github.com/felixweinberger)! - Allow additional JSON | ||
| Schema properties in elicitInput's requestedSchema type by adding .catchall(z.unknown()), matching the pattern used by inputSchema. This fixes type incompatibility when using Zod v4's .toJSONSchema() output which includes extra properties like $schema and additionalProperties. | ||
|
|
||
| ## 2.0.0-alpha.1 | ||
|
|
There was a problem hiding this comment.
🔴 The isCallToolResult guard (introduced in PR #1842, now published in this alpha.2 version bump) returns false for any object missing an explicit content key, but CallToolResultSchema.safeParse({}).success returns true because content carries .default([]). The migration guide at docs/migration.md:450-454 explicitly presents isCallToolResult(value) as a direct drop-in replacement for CallToolResultSchema.safeParse(value).success; that equivalence claim is incorrect and will silently produce false negatives for users following the guide with objects lacking an explicit content field.
Extended reasoning...
What the bug is: isCallToolResult in packages/core/src/types/guards.ts contains an early-exit check !("content" in value) that short-circuits for any object without a content own-property, even if that object would parse successfully via the schema. Meanwhile, CallToolResultSchema defines content: z.array(ContentBlockSchema).default([]), so CallToolResultSchema.safeParse({}).success === true. The two are not equivalent.
The migration guide makes an explicit, incorrect equivalence claim: docs/migration.md:450-454 presents isCallToolResult(value) as a direct drop-in replacement for CallToolResultSchema.safeParse(value).success. Any user migrating by substituting isCallToolResult will silently get false for a valid CallToolResult whose content field was omitted in the raw object — e.g. a tool response carrying only structuredContent before going through Zod parsing.
Addressing the refutations: The refutations correctly observe that (a) the TypeScript output type for CallToolResult has content as a required field (Zod's .default([]) makes it required in the output, optional only in input), (b) the MCP spec says content is "always present" for backwards compatibility, and (c) values returned by client.callTool() have already been Zod-parsed so content will be present. These are valid, and they explain why real-world breakage is narrow in normal SDK usage.
However, the refutations do not resolve the issue: the migration guide's equivalence claim is stated unconditionally, without the caveat that it only holds for already-parsed values. A developer writing a custom validator, testing raw server responses, or constructing a CallToolResult-shaped object manually before parsing will encounter a false negative. The guard is intentionally stricter than safeParse, but the migration guide does not communicate this distinction.
Concrete proof: (1) Raw object: { isError: false } — no content key, but valid per schema since content defaults to []. (2) CallToolResultSchema.safeParse({ isError: false }).success returns true. (3) isCallToolResult({ isError: false }) returns false due to the !("content" in value) early exit. (4) A user following the migration guide who swaps step 2 for step 3 silently rejects a valid value.
Impact and fix: The bug's practical blast radius is limited to objects that have not been Zod-parsed yet; in the normal SDK flow those are rare. But this is a published migration guide making a false API contract claim. The fix is either to remove the !("content" in value) early-exit in guards.ts (making the guard consistent with safeParse semantics), or to update the migration guide to clearly document that isCallToolResult is stricter and requires content to be explicitly present in the raw object.
| ## 2.0.0-alpha.3 | ||
|
|
||
| ### Patch Changes | ||
|
|
||
| - [#1655](https://github.com/modelcontextprotocol/typescript-sdk/pull/1655) [`1eb3123`](https://github.com/modelcontextprotocol/typescript-sdk/commit/1eb31236e707c4f4ab9234d87db21ab3f34bf0bc) Thanks [@nielskaspers](https://github.com/nielskaspers)! - fix(client): append custom | ||
| Accept headers to spec-required defaults in StreamableHTTPClientTransport | ||
|
|
There was a problem hiding this comment.
🟡 PR #1843 (commit 7ba58da) added the ./validators/cf-worker sub-path export and CfWorkerJsonSchemaValidator to both @modelcontextprotocol/client and @modelcontextprotocol/server without including a changeset file, so the alpha.3 CHANGELOGs for both packages make no mention of this new feature. Users upgrading from alpha.2 to alpha.3 have no way to discover the new CF Workers validator integration from the release notes. Before merging, manually add a CHANGELOG entry to both packages/client/CHANGELOG.md and packages/server/CHANGELOG.md documenting the new ./validators/cf-worker export path and CfWorkerJsonSchemaValidator.
Extended reasoning...
CF Workers validator feature silently absent from alpha.3 CHANGELOG
What the bug is and how it manifests
PR #1843 (commit 7ba58da) landed two concrete, user-visible additions to both @modelcontextprotocol/client and @modelcontextprotocol/server: a new ./validators/cf-worker sub-path export (visible in both packages/client/package.json and packages/server/package.json under exports) and a CfWorkerJsonSchemaValidator class exposed through it. However, the PR author did not create a changeset file for these changes. Because Changesets generates CHANGELOG entries exclusively from changeset files, the alpha.3 CHANGELOG for both packages says nothing about this feature — the client CHANGELOG only mentions the Accept-header fix (PR #1655) and the server CHANGELOG only mentions the stack-overflow fix (PR #1788).
The specific code path
packages/client/package.json (line 28–31) and packages/server/package.json (line 28–31) both contain the "./validators/cf-worker" export entry added by commit 7ba58da. Searching .changeset/ in that commit yields zero results — the three new changeset slugs added in .changeset/pre.json (fix-streamable-close-reentrant, odd-forks-enjoy, zod-json-schema-compat) cover entirely different features. Neither the odd-forks-enjoy slug (which produced the Accept-header entry) nor any other slug mentions CF Workers.
Addressing the refutations
Two verifiers argued this is "not actionable" because the Changesets workflow can only produce entries from changeset files and the version-bump PR cannot retroactively create them. This reasoning is correct about how Changesets generates content automatically, but misses the point: CHANGELOG.md files are plain text and can be edited manually before a PR is merged. Version-bump PRs that accidentally missed changesets are routinely fixed by hand-editing the CHANGELOG before the merge/publish. Since this PR has not yet been merged, the omission is still correctable here. The workflow contract is with the automation, not with the maintainers — maintainers always retain the ability to amend the generated text.
Why existing code does not prevent it
There is no CI check that validates CHANGELOG completeness against the set of merged commits between two version tags. The Changesets action produces whatever its inputs say; it cannot detect omitted changeset files. The alpha pre-release mode does not lower the practical importance of this: the ./validators/cf-worker export is the only documented way to use CF Workers JSON schema validation, and alpha users actively track release notes to find new APIs.
Step-by-step proof
- Check commit 7ba58da:
git show 7ba58da -- .changeset/returns empty — no changeset file was added. - Open
packages/client/CHANGELOG.mdlines 3–14: only one entry under2.0.0-alpha.3, referencing PR fix(client): preserve custom Accept headers in StreamableHTTPClientTransport #1655 (Accept headers). No mention of./validators/cf-workerorCfWorkerJsonSchemaValidator. - Open
packages/server/CHANGELOG.mdlines 3–11: only one entry under2.0.0-alpha.3, referencing PR fix: prevent stack overflow in transport close with re-entrancy guard #1788 (stack overflow fix). Same omission. - Open
packages/client/package.jsonlines 28–31:"./validators/cf-worker"export is present and will ship in alpha.3. - A user reading the alpha.3 release notes finds no indication this export exists.
How to fix
Manually add a patch-change entry to both packages/client/CHANGELOG.md and packages/server/CHANGELOG.md under 2.0.0-alpha.3, crediting PR #1843. For example:
- [#1843](https://github.com/modelcontextprotocol/typescript-sdk/pull/1843) Add `./validators/cf-worker` sub-path export and `CfWorkerJsonSchemaValidator` for Cloudflare Workers environments. Import from `@modelcontextprotocol/client/validators/cf-worker` (or `/server/`) to use the workerd-compatible JSON Schema validator.This is a one-time manual correction; no changeset infrastructure changes are needed.
fe53cda to
a1b6ce1
Compare
| ## 2.0.0-alpha.3 | ||
|
|
||
| ### Patch Changes | ||
|
|
||
| - [#1655](https://github.com/modelcontextprotocol/typescript-sdk/pull/1655) [`1eb3123`](https://github.com/modelcontextprotocol/typescript-sdk/commit/1eb31236e707c4f4ab9234d87db21ab3f34bf0bc) Thanks [@nielskaspers](https://github.com/nielskaspers)! - fix(client): append custom | ||
| Accept headers to spec-required defaults in StreamableHTTPClientTransport | ||
|
|
||
| Custom Accept headers provided via `requestInit.headers` are now appended to the spec-mandated Accept types instead of being overwritten. This ensures the required media types (`application/json, text/event-stream` for POST; `text/event-stream` for GET SSE) are always present | ||
| while allowing users to include additional types for proxy/gateway routing. | ||
|
|
||
| ## 2.0.0-alpha.2 | ||
|
|
There was a problem hiding this comment.
🟡 PR #1842 (commit 1eb80c4, 'v2: add guard methods') introduced new public API type guards — at minimum isCallToolResult and isJSONRPCResponse — to @modelcontextprotocol/client and @modelcontextprotocol/server without a changeset file, so these exports are absent from all alpha.3 CHANGELOGs. Before merging, manually add CHANGELOG entries for the new guard functions in packages/client/CHANGELOG.md and packages/server/CHANGELOG.md under 2.0.0-alpha.3.
Extended reasoning...
Guard methods from PR #1842 absent from alpha.3 CHANGELOGs
What the bug is and how it manifests
PR #1842 (commit 1eb80c4, 'v2: add guard methods') added new type guard functions to packages/core/src/types/guards.ts and exported them via packages/core/src/exports/public/index.ts. Because both packages/client/src/index.ts and packages/server/src/index.ts do export * from '@modelcontextprotocol/core/public', these guards became part of the published public API of both @modelcontextprotocol/client@2.0.0-alpha.3 and @modelcontextprotocol/server@2.0.0-alpha.3. However, PR #1842 did not create a changeset file, so the Changesets action generated no CHANGELOG entries for these additions.
The specific code path
The guards flow: packages/core/src/types/guards.ts → re-exported from packages/core/src/exports/public/index.ts (lines 107–115) → consumed via export * from '@modelcontextprotocol/core/public' in both packages/client/src/index.ts and packages/server/src/index.ts. Verifiers confirmed that at minimum isCallToolResult and isJSONRPCResponse are newly introduced in this commit; most other JSONRPC guards in the list may have pre-existed. No .changeset/ file appears in the commit, confirmed by git show 1eb80c4 -- .changeset/ returning empty.
Why existing code does not prevent it
There is no CI check that validates CHANGELOG completeness against the set of merged commits. The Changesets action only generates entries from changeset files it finds; it cannot detect commits that should have had a changeset but did not. This is the same root cause as the CF Workers CHANGELOG omission (PR #1843).
Impact
Users upgrading from alpha.2 to alpha.3 who read the release notes have no way to discover these new type guard exports. Alpha users actively track release notes precisely to find new APIs, so undocumented public additions reduce the practical value of the alpha program. The omission is a documentation gap with no functional impact.
Step-by-step proof
git show 1eb80c4 -- .changeset/returns nothing — PRv2: add guard methods #1842 created no changeset file.- Open
packages/client/CHANGELOG.mdlines 3–14: the only alpha.3 entry is for PR fix(client): preserve custom Accept headers in StreamableHTTPClientTransport #1655 (Accept headers). No mention of any guard function. - Open
packages/server/CHANGELOG.mdlines 3–11: the only alpha.3 entry is for PR fix: prevent stack overflow in transport close with re-entrancy guard #1788 (stack overflow fix). Same omission. - Open
packages/core/src/exports/public/index.tslines 107–115:isCallToolResult,isJSONRPCResponse, and related guards are exported. - A user reading the alpha.3 release notes finds zero indication that type guards were added to either published package.
How to fix
Manually add a patch-change entry to both packages/client/CHANGELOG.md and packages/server/CHANGELOG.md under 2.0.0-alpha.3, crediting PR #1842. The CHANGELOG files are plain text and can be amended before this PR is merged, as was done for other missed changeset entries.
3f8a438 to
0cefcf3
Compare
0cefcf3 to
2bc21b7
Compare
| { | ||
| "name": "@modelcontextprotocol/server", | ||
| "version": "2.0.0-alpha.2", | ||
| "version": "2.0.0-alpha.3", | ||
| "description": "Model Context Protocol implementation for TypeScript - Server package", | ||
| "license": "MIT", | ||
| "author": "Anthropic, PBC (https://anthropic.com)", |
There was a problem hiding this comment.
🔴 The server's ./_shims browser export condition points to dist/shimsWorkerd.mjs — the same file as the workerd condition — which contains a bare external import of @cfworker/json-schema, a package absent from dependencies and peerDependencies. Any user who bundles @modelcontextprotocol/server targeting a browser environment (Vite, webpack, Rollup, esbuild all apply the browser condition automatically) will get a module-not-found error for @cfworker/json-schema at build or runtime. Fix by either (a) adding @cfworker/json-schema as an optional peerDependency so consumers are warned to install it, or (b) removing the browser export condition so browser bundlers fall through to the default node shim.
Extended reasoning...
Server browser export condition routes to CF Workers shim, missing required dependency
What the bug is and how it manifests
packages/server/package.json maps the ./_shims export with the browser condition to dist/shimsWorkerd.d.mts / dist/shimsWorkerd.mjs — the exact same files used for the workerd condition. Standard browser bundlers (Vite, webpack, Rollup, esbuild) automatically select the browser export condition. Any developer bundling @modelcontextprotocol/server for a browser target will silently load shimsWorkerd.mjs, which contains a bare top-level ESM import of @cfworker/json-schema. That package is only listed in devDependencies of the server package — not in dependencies or peerDependencies — so no consumer will have it installed, causing a module-not-found error at build-time or runtime.
The specific code path
shimsWorkerd.ts line 6 re-exports CfWorkerJsonSchemaValidator as DefaultJsonSchemaValidator from @modelcontextprotocol/core. packages/core/src/validators/cfWorkerProvider.ts line 11 contains a static top-level import: import { Validator } from '@cfworker/json-schema'. The tsdown.config.ts for the server sets noExternal: ['@modelcontextprotocol/core'] (inlining core), but @cfworker/json-schema is not in noExternal, so the build leaves a bare external import in the output bundle.
Why existing code doesn't prevent it
The client package (packages/client/package.json) correctly provides a distinct shimsBrowser.mjs for the browser condition. The server has no shimsBrowser.ts at all (only shimsNode.ts and shimsWorkerd.ts). PR #1843 which added CF Workers support created the workerd/browser routing but neglected to either provide a server-specific browser shim or add @cfworker/json-schema to peerDependencies.
Addressing the refutation
One verifier argued that the browser→shimsWorkerd routing is intentional because the server needs CfWorkerJsonSchemaValidator (AJV has Node.js dependencies and cannot run in browsers) and both environments need identical behavior — unlike the client which requires distinct shimsBrowser for CORS_IS_POSSIBLE semantics. This argument about intent is plausible, but it does not resolve the functional bug: regardless of whether the routing is intentional, @cfworker/json-schema is not in dependencies or peerDependencies of the published server package, so no browser bundler user will have it installed. The intent and the implementation are misaligned.
Step-by-step proof
- Developer runs
npm install @modelcontextprotocol/server—@cfworker/json-schemais not installed (only in devDeps, never for consumers). - Developer's Vite config targets browser; Vite resolves
./_shimsvia thebrowserexport condition → getsdist/shimsWorkerd.mjs. dist/shimsWorkerd.mjscontainsimport { Validator } from '@cfworker/json-schema'as a top-level static import.- Vite (or webpack/Rollup/esbuild) cannot resolve
@cfworker/json-schema→ build error or runtimeModule not foundcrash. - This affects all browser-bundled server users, not just CF Workers deployments.
How to fix
Option A (minimal): Add @cfworker/json-schema as an optional peerDependency in packages/server/package.json, matching the pattern in packages/core/package.json. This at minimum warns consumers to install it.
Option B (correct): Create a packages/server/src/shimsBrowser.ts that either provides a pure-browser-compatible JSON schema validator or re-exports from the node shim (process is unavailable in browsers anyway), then point the browser export condition to dist/shimsBrowser.mjs. This matches the client package's architecture and removes the dependency on @cfworker/json-schema for standard browser users entirely.
Option C (simplest safe fix): Remove the browser export condition from ./_shims in packages/server/package.json so browser bundlers fall through to the default (node) shim. If the node shim works in browser contexts for the server use case, this is the least-risk change.
🔬 also observed by 3035194589
| { | ||
| "name": "@modelcontextprotocol/server", | ||
| "version": "2.0.0-alpha.2", | ||
| "version": "2.0.0-alpha.3", | ||
| "description": "Model Context Protocol implementation for TypeScript - Server package", | ||
| "license": "MIT", | ||
| "author": "Anthropic, PBC (https://anthropic.com)", |
There was a problem hiding this comment.
🟣 Pre-existing: packages/server/package.json and packages/client/package.json only list zod in runtime dependencies, but both packages bundle AjvJsonSchemaValidator (via noExternal: ['@modelcontextprotocol/core']) with bare external ESM imports for ajv and ajv-formats that are never installed for npm consumers. Any Node.js user who creates a Server() or Client() instance without a custom jsonSchemaValidator will receive a module-not-found error for ajv at startup; fix by adding ajv and ajv-formats to the runtime dependencies of both packages.
Extended reasoning...
ajv/ajv-formats missing from published runtime dependencies
What the bug is and how it manifests
Both @modelcontextprotocol/server and @modelcontextprotocol/client ship with only zod in their runtime dependencies. However, the built dist/shimsNode.mjs for each package contains top-level bare ESM import statements such as import { Ajv } from "ajv" and import _addFormats from "ajv-formats". Because these modules are not listed in the published package's dependencies, npm/pnpm will never install them for a downstream consumer. The first time any Node.js code path reaches module initialization for shimsNode.mjs, the JavaScript runtime throws ERR_MODULE_NOT_FOUND for ajv.
The specific code path that triggers it
The dependency chain is:
packages/server/tsdown.config.tssetsnoExternal: ['@modelcontextprotocol/core'], which inlines all of core's TypeScript source directly into the server bundle.packages/core/src/validators/ajvProvider.ts(lines 5–6) has static top-level imports:import { Ajv } from 'ajv'andimport _addFormats from 'ajv-formats'. These remain as external references in the inlined bundle becauseajvis NOT listed innoExternal.packages/server/src/shimsNode.tsexportsAjvJsonSchemaValidator as DefaultJsonSchemaValidator— bundled intodist/shimsNode.mjs.packages/server/src/server/server.ts(line 125) executesthis._jsonSchemaValidator = options?.jsonSchemaValidator ?? new DefaultJsonSchemaValidator()for everyServerinstantiation that omits the option.packages/server/package.jsonlists only"zod": "catalog:runtimeShared"underdependencies— noajvorajv-formats.
The same chain applies identically to @modelcontextprotocol/client and its shimsNode.mjs.
Why existing code doesn't prevent it
packages/core/package.json does list ajv and ajv-formats in its own dependencies. However, core is "private": true and is never published to npm. Its dependency declarations are invisible to external consumers. The only package.json files that matter to npm consumers are the published packages/server/package.json and packages/client/package.json, and both omit ajv entirely. tsdown's platform: 'node' configuration treats all non-noExternal bare module imports as external by design, so the import statements are preserved verbatim in the bundle output.
What the impact would be
This breaks virtually all Node.js consumers of the alpha packages. The DefaultJsonSchemaValidator (i.e., AjvJsonSchemaValidator) is the unconditional default for both Server and Client on Node.js. A user who runs npm install @modelcontextprotocol/server and writes new Server(...) will immediately get ERR_MODULE_NOT_FOUND: Cannot find package 'ajv' — before any MCP logic executes. The only workaround is passing { jsonSchemaValidator: customValidator } on every constructor call, which is undocumented behavior that no alpha user would know to do.
How to fix it
Add ajv and ajv-formats to the runtime dependencies in both packages/server/package.json and packages/client/package.json:
"dependencies": {
"ajv": "catalog:runtimeShared",
"ajv-formats": "catalog:runtimeShared",
"zod": "catalog:runtimeShared"
}Alternatively, add both to noExternal in each package's tsdown.config.ts to inline them into the bundle, which avoids the peer-install requirement entirely.
Step-by-step proof
- Install the published package:
npm install @modelcontextprotocol/server@2.0.0-alpha.3. Onlyzodis installed as a transitive dependency;ajvis absent fromnode_modules. - Write:
import { Server } from '@modelcontextprotocol/server';— this resolvesdist/index.mjswhich imports from./_shims. ./_shimson Node.js resolves todist/shimsNode.mjs, which begins withimport { Ajv } from "ajv"as its first statement.- Node.js attempts to resolve
ajv→ not found → throwsERR_MODULE_NOT_FOUNDimmediately at module load time, before any user code executes. new Server(...)is never reached; the entire import fails.
| { | ||
| "name": "@modelcontextprotocol/client", | ||
| "version": "2.0.0-alpha.2", | ||
| "version": "2.0.0-alpha.3", |
There was a problem hiding this comment.
🔴 The browser export condition for ./_shims in packages/client/package.json routes to dist/shimsBrowser.mjs, which contains a bare external ESM import of @cfworker/json-schema (inlined from core via noExternal, but with @cfworker/json-schema left external). Since @cfworker/json-schema is only in devDependencies and not in dependencies or peerDependencies, any browser bundler (Vite, webpack, Rollup, esbuild) that resolves the browser export condition will fail with a module-not-found error. Fix by adding @cfworker/json-schema as an optional peerDependency in packages/client/package.json.
Extended reasoning...
Client shimsBrowser.mjs requires @cfworker/json-schema but it is not in runtime dependencies
What the bug is and how it manifests
packages/client/package.json exports the ./_shims entry with a browser condition that points to dist/shimsBrowser.mjs. This file is built from packages/client/src/shimsBrowser.ts, which exports CfWorkerJsonSchemaValidator as DefaultJsonSchemaValidator from @modelcontextprotocol/core (line 6). packages/core/src/validators/cfWorkerProvider.ts has a static top-level import: import { Validator } from '@cfworker/json-schema' (line 11). The tsdown.config.ts sets noExternal: ['@modelcontextprotocol/core'], so all of core is inlined into the client bundle — but @cfworker/json-schema is NOT in noExternal and therefore remains as a bare external ESM import statement in the emitted dist/shimsBrowser.mjs.
The specific code path that triggers it
Browser bundlers (Vite, webpack, Rollup, esbuild) automatically select the browser export condition when targeting browsers. Any developer building a browser application that imports @modelcontextprotocol/client will have their bundler resolve ./_shims → dist/shimsBrowser.mjs, which immediately encounters import { Validator } from '@cfworker/json-schema' as the first top-level import. Since @cfworker/json-schema is only in devDependencies of packages/client/package.json and not in dependencies or peerDependencies, it is never installed for npm consumers.
Why existing code does not prevent it
packages/core/package.json does list @cfworker/json-schema as an optional peerDependency. However, core is "private": true and never published to npm, so its dependency declarations are invisible to external consumers. The only package.json that matters to browser bundler consumers is packages/client/package.json, and there @cfworker/json-schema appears exclusively in devDependencies. No package manager will warn users to install it, and no bundler will find it in node_modules.
What the impact is
Any developer targeting a browser environment with @modelcontextprotocol/client@2.0.0-alpha.3 will receive a module-not-found error for @cfworker/json-schema at build time (static bundlers like Vite/esbuild) or at runtime. The browser condition is applied automatically — no explicit import of the validator sub-path is needed to trigger this. This bug is distinct from the workerd condition issue (bug_003) and the server browser condition issue (bug_008); the client's shimsBrowser.ts has CORS_IS_POSSIBLE=true semantics distinct from the workerd shim but still uses CfWorkerJsonSchemaValidator, so the same missing dependency applies here.
Step-by-step proof
- Developer runs npm install @modelcontextprotocol/client@2.0.0-alpha.3 — @cfworker/json-schema is not installed (only in devDependencies, never transitively installed for consumers).
- Developer's Vite/webpack config targets browser; the bundler resolves ./_shims via the browser export condition → selects dist/shimsBrowser.mjs.
- dist/shimsBrowser.mjs contains a bare top-level import { Validator } from '@cfworker/json-schema' statement.
- Bundler cannot resolve @cfworker/json-schema — build fails with module-not-found, or runtime throws ERR_MODULE_NOT_FOUND.
- new Client() is never reached.
How to fix
Add @cfworker/json-schema as an optional peerDependency in packages/client/package.json. This ensures package managers warn browser-targeting consumers to install @cfworker/json-schema and makes the runtime requirement visible in the published package metadata. Alternatively, add @cfworker/json-schema to noExternal in the client's tsdown.config.ts to bundle it directly.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
mainis currently in pre mode so this branch has prereleases rather than normal releases. If you want to exit prereleases, runchangeset pre exitonmain.Releases
@modelcontextprotocol/client@2.0.0-alpha.3
Patch Changes
#1655
1eb3123Thanks @nielskaspers! - fix(client): append customAccept headers to spec-required defaults in StreamableHTTPClientTransport
Custom Accept headers provided via
requestInit.headersare now appended to the spec-mandated Accept types instead of being overwritten. This ensures the required media types (application/json, text/event-streamfor POST;text/event-streamfor GET SSE) are always presentwhile allowing users to include additional types for proxy/gateway routing.
@modelcontextprotocol/server@2.0.0-alpha.3
Patch Changes
df4b6ccThanks @claygeo! - Prevent stack overflow inStreamableHTTPServerTransport.close() with re-entrant guard
@modelcontextprotocol/core@2.0.0-alpha.2
Patch Changes
866c08dThanks @felixweinberger! - Allow additional JSONSchema properties in elicitInput's requestedSchema type by adding .catchall(z.unknown()), matching the pattern used by inputSchema. This fixes type incompatibility when using Zod v4's .toJSONSchema() output which includes extra properties like $schema and additionalProperties.